﻿using System.Collections.Generic;
using System.Text;

namespace smp.QueryDb.Structure
{
    public class Join
    {
        private readonly List<Join> _subJoins = new List<Join>();
        private readonly StringBuilder _joinDeclaration;
        private readonly Table _joinTable;

        public Join(Table table)
        {
            _subJoins.Clear();
            _joinTable = table;
            _joinDeclaration = new StringBuilder();
            _joinDeclaration.AppendFormat("JOIN {0} ", table.FromStatement);
        }

        internal string JoinTableDeclaration { get { return _joinTable.Declaration; } }

        internal string JoinDeclaration {get { return _joinDeclaration.ToString(); } }

        internal void FiniliseJoinWithBaseDeclaration(string baseDeclaration)
        {
            _joinDeclaration.Replace("#BASE#", baseDeclaration);
        }

        internal void SetIsOuter()
        {
            _joinDeclaration.Insert(0, "LEFT ");
        }

        internal void SetRightColumn(string columnName, string equalityOperator)
        {
            _joinDeclaration.AppendFormat("{0} {1}.[{2}] ", equalityOperator, _joinTable.Declaration, columnName);
        }

        internal bool IsTableDeclared(string tableDeclaration)
        {
            if(_joinTable.IsTableDeclared(tableDeclaration))
                return true;
            if(_subJoins.Count == 0)
                return false;
            foreach(var subJoin in _subJoins)
            {
                if(subJoin.IsTableDeclared(tableDeclaration))
                    return true;
            }
            return false;
        }

        public IJoinOn On(Column leftColumn)
        {
            _joinDeclaration.Append("ON ");
            _joinDeclaration.AppendFormat("#BASE#.[{0}] ", leftColumn.Name);
            return new JoinOn(this);
        }

        public IJoinOn And(Column leftColumn)
        {
            _joinDeclaration.Append("AND ");
            _joinDeclaration.AppendFormat("#BASE#.[{0}] ", leftColumn.Name);
            return new JoinOn(this);
        }

        public IJoinOn Or(Column leftColumn)
        {
            _joinDeclaration.Append("OR ");
            _joinDeclaration.AppendFormat("#BASE#.[{0}] ", leftColumn.Name);
            return new JoinOn(this);
        }

        public Join SubJoin(Join join)
        {
            join.FiniliseJoinWithBaseDeclaration(_joinTable.Declaration);
            _subJoins.Add(join);
            _joinDeclaration.Append(join.JoinDeclaration);
            return this;
        }

        public Join SubOuterJoin(Join join)
        {
            join.SetIsOuter();
            join.FiniliseJoinWithBaseDeclaration(_joinTable.Declaration);
            _subJoins.Add(join);
            _joinDeclaration.Append(join.JoinDeclaration);
            return this;
        }
    }

    public interface IJoinOn
    {
        Join IsEqualTo(Column rightColumn);

        Join IsGreaterThan(Column rightColumn);

        Join IsGreaterThanOrEqualTo(Column rightColumn);

        Join IsLessThan(Column rightColumn);

        Join IsLessThanOrEqualTo(Column rightColumn);

        Join IsNotEqualTo(Column rightColumn);
    }

    internal class JoinOn : IJoinOn
    {
        private readonly Join _join;
        
        internal JoinOn(Join join)
        {
            _join = join;
        }

        public Join IsEqualTo(Column rightColumn)
        {
            _join.SetRightColumn(rightColumn.Name, "=");
            return _join;
        }
        
        public Join IsGreaterThan(Column rightColumn)
        {
            _join.SetRightColumn(rightColumn.Name, ">");
            return _join;
        }

        public Join IsGreaterThanOrEqualTo(Column rightColumn)
        {
            _join.SetRightColumn(rightColumn.Name, ">=");
            return _join;
        }

        public Join IsLessThan(Column rightColumn)
        {
            _join.SetRightColumn(rightColumn.Name, "<");
            return _join;
        }

        public Join IsLessThanOrEqualTo(Column rightColumn)
        {
            _join.SetRightColumn(rightColumn.Name, "<=");
            return _join;
        }

        public Join IsNotEqualTo(Column rightColumn)
        {
            _join.SetRightColumn(rightColumn.Name, "!=");
            return _join;
        }
    }
}
